package com.jtyoui.jsns;

import com.jtyoui.bean.Words;
import com.jtyoui.isns.Sns;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 训练文本，提取词语
 *
 * @author Jtyoui
 * @version 18.5.10
 */
public class JSns implements Sns {

    private int split_num;//训练词语的候选词
    private int sum;//总文本字数
    private Map<String, Words> sns_word = new HashMap<>();//封装每个词对象，key是词、value是词对象
    private float oneFrequency;//增加一个次数的频率==1/sum
    private Set<String> bookSet = new TreeSet<>();//书名词

    /**
     * 初始化指定训练词语的候选词
     *
     * @param split_num 训练词语的候选词
     */
    public JSns(int split_num) {
        this.split_num = split_num;
    }

    /**
     * 将句子拆分成每个词语对象
     *
     * @param word 句子
     */
    @Override
    public void split(String word) {
        for (int i = 0; i < word.length(); i++) {
            for (int j = 1; j <= split_num; j++) {//词语最少一个字或以上-->词语的区间[1,split_num）
                if (i + j <= word.length()) { //当前游标加当前分词字数不得大于总句子字数
                    String key = word.substring(i, i + j);//获得当前分词
                    Words words = sns_word.get(key);//获得当前分词对象,对面存在不更新，对象不存在需要实例化在更新
                    if (words != null) {
                        words.setKey(key);//候选词
                        words.setTotal(words.getTotal() + 1);//次数加一
                        words.setFrequency(words.getFrequency() + this.oneFrequency);//频率+oneFrequency
                        if (i != 0) {//开头没有
                            Character left = word.charAt(i - 1);//左字符
                            words.setLeftWord(left);//加入左字符
                            if (words.getLeftCount().containsKey(left))
                                words.setLeftCount(left, words.getLeftCount().get(left) + 1);//添加左字次数
                            else
                                words.setLeftCount(left, 1);
                        }
                        if (i + j != word.length()) {//结尾没有
                            Character right = word.charAt(i + j);
                            words.setRightWord(right);//加入右字符
                            if (words.getRightCount().containsKey(right))
                                words.setRightCount(right, words.getRightCount().get(right) + 1);//添加右字次数
                            else
                                words.setRightCount(right, 1);
                        }
                    } else {
                        words = new Words();//实例化对象
                        words.setKey(key);//候选词
                        words.setTotal(1);//次数
                        words.setFrequency(1.0f / sum);//词频
                        if (i != 0) {
                            words.setLeftWord(word.charAt(i - 1));//最左字符没有
                            words.setLeftCount(word.charAt(i - 1), 1);
                        }
                        if (i + j != word.length()) {
                            words.setRightWord(word.charAt(i + j));//最右字符没有
                            words.setRightCount(word.charAt(i + j), 1);
                        }
                        sns_word.put(key, words);//更新对象
                    }
                } else
                    break;
            }
        }

    }

    /**
     * 清洗数据，除去非中文字符
     *
     * @param string 句子
     * @return 清洗过后的句子全是中文字符
     */
    public String[] clean(String string) {
        Pattern pattern = Pattern.compile("(?<=《)[^》]+(?=》)");
        Matcher matcher = pattern.matcher(string);
        while (matcher.find()) {
            String name = matcher.group();
            if (name.length() > 1)
                bookSet.add(name);
        }
        string = string.replaceAll("[=【】a-zA-Z()“”〔〕《》（）0-9{}+/* ]|\\s*", "");
        String[] list = string.split("[，。,；:：;、？?！!]");
        this.sum = string.length();
        this.oneFrequency = 1.0f / this.sum;//获得总分之一的频率
        return list;
    }

    /**
     * 读取字符串
     *
     * @param file 文件对象
     * @return 读取后的字符串
     */
    public String readToString(File file) {
        return readToString(file.getAbsolutePath());
    }

    /**
     * 读取字符串
     *
     * @param fileName 文本路径
     * @return 读取后的字符串
     */
    public String readToString(String fileName) {
        String encoding = "UTF-8";//默认编码是UTF-8
        File file = new File(fileName);
        byte[] bytes = new byte[(int) file.length()];//将所有字符一次性读取
        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            this.sum = fileInputStream.read(bytes);
            this.oneFrequency = 1.0f / this.sum;//获得总分之一的频率
            if (sum == -1)
                throw new IOException("读取文本失败");
            fileInputStream.close();
            return new String(bytes, encoding);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 处理词语对象
     */
    @Override
    public void handle() {
        for (String key : sns_word.keySet()) {
            Words words = sns_word.get(key);//获取词语对象
            float leftAll = 0.0f;//左自由度
            float rightAll = 0.0f;//右自由度
            int len = key.length();
            if (len > 1) {
                //左凝聚度：p(电影院)/p(电)*p(影院)
                float left = words.getFrequency() / (sns_word.get(key.substring(0, 1)).getFrequency() * sns_word.get(key.substring(1, len)).getFrequency());
                //右凝聚度：p(电影院)/p(电影)*p(院)
                float right = words.getFrequency() / (sns_word.get(key.substring(0, len - 1)).getFrequency() * sns_word.get(key.substring(len - 1, len)).getFrequency());
                //计算自由度
                for (Character cLeft : words.getLeftWord()) {
                    Words wordLeft = sns_word.get(String.valueOf(cLeft));
                    float pLeft = wordLeft.getFrequency();//左字符概率
                    leftAll -= (Math.log(pLeft) / Math.log(2)) * pLeft;  //左自由度:-log2(p(电影院))*p(电影院)
                }
                for (Character cRight : words.getRightWord()) {
                    Words wordRight = sns_word.get(String.valueOf(cRight));
                    float pRight = wordRight.getFrequency();//右字符概率
                    rightAll -= (Math.log(pRight) / Math.log(2)) * pRight;//右自由度:-log2(p(电影院))*p(电影院)
                }
                words.setSolidify(left < right ? left : right);//选取最低的凝聚度
                words.setFree(leftAll < rightAll ? leftAll : rightAll);//选取最低的自由度
                shift(words, key);//计算偏移度
            }
        }
    }


    /**
     * 计算最低的偏移度
     *
     * @param words 词语对象
     * @param key   词语
     */
    private void shift(Words words, String key) {
        int len = key.length();
        float left = sns_word.get(key.substring(0, 1)).getTotal() * sns_word.get(key.substring(1, len)).getTotal();
        float right = sns_word.get(key.substring(0, len - 1)).getTotal() * sns_word.get(key.substring(len - 1, len)).getTotal();
        words.setShift((left < right ? left : right) / sns_word.get(key).getTotal());
    }


    /**
     * 获得词对象
     *
     * @return 词对象
     */
    public Map<String, Words> getSns_word() {
        return sns_word;
    }

    /**
     * 获得书名词语
     *
     * @return 书名词语
     */
    public Set<String> getBookSet() {
        return bookSet;
    }
}